/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_GRAPHICS_OBJECT_H
#define SX_GRAPHICS_OBJECT_H

#include <sxString.h>
#include <sxTransformation.h>
#include <sxColor3.h>
#include <sxColor4.h>

namespace Sx {
namespace Graphics {

/*
 * Base class of all abstract graphics objects. Graphics objects are typically
 * defined in terms of a generalized idea. For example a mesh is a graphics
 * object composed of textured faces. Since most graphics objects have a 
 * physical representation, the mesh class can be paired with a mesh physical
 * representation (that contains vertices) to form an Archetype. The definition
 * of the mesh (faces) and the physical representation (vertices) will define
 * a set of faces with vertices that can be rendererd in a graphics viewport.
 */
class GraphicsObject {
public:
	GraphicsObject(bool visible = true, bool selected = false);
	GraphicsObject(const String& name, bool visible = true, bool selected = false);
	GraphicsObject(const String& name, float x, float y, float z);
	virtual ~GraphicsObject();

	/*
	 * All graphics objects have their own initialization requirements. The
	 * construct function should be implemented to create the abstract
	 * representation of the object (if needed). The update function should
	 * be an implementation that updates this definition (if needed) during
	 * the execution of a graphics environment (dt represents the time elapsed
	 * since the last update call; however it can be used for any kind of time
	 * update).
	 */
	virtual bool construct() = 0;
	virtual void update(float dt) = 0;

	void setName(const String& name);
	void setVisible(bool visible);
	void setSelected(bool selected);

	/*
	 * Color modification functions.
	 */
	void setColor(const Color3f& color);
	void setColor(const Color4f& color);
	void setColor(float r, float g, float b);
	void setColor(float r, float g, float b, float a);

	void hide();
	void show();
	void select();
	void deselect();

	/* Selection distance modification. */
	void setPointSelectionDistance(float distance);
	void setLineSelectionDistance(float distance);

	/*
	 * The following functions allow a displacement to be applied to this
	 * object. The existing definition of the position is only modified by the
	 * provided value.
	 */
	bool addPositionX(float x);
	bool addPositionY(float y);
	bool addPositionZ(float z);
	bool addPosition(float x, float y, float z);
	bool addPosition(const Eigen::Vector3f& displacement);

	/*
	 * Functions for modifying the location of the object. These functions will
	 * erase the existing definition of the component to be modified. The
	 * position will then be updated with the new value.
	 */
	bool setPositionX(float x);
	bool setPositionY(float y);
	bool setPositionZ(float z);
	bool setPosition(float x, float y, float z);
	bool setPosition(const Eigen::Vector3f& position);

	/*
	 * Functions for modifying the scale of the object.
	 */
	bool setScaleX(float x);
	bool setScaleY(float y);
	bool setScaleZ(float z);
	bool setScale(float x, float y, float z);
	bool setScale(const Eigen::Vector3f& scale);

	/*
	 * The following set of functions modify the rotation of this object in
	 * its local coordinates. 
	 */
	bool addRotationX(float angle);
	bool addRotationY(float angle);
	bool addRotationZ(float angle);
	bool addRotation(const Eigen::Quaternionf& rotation);
	
	/*
	 * This function completely erases the rotation of this object and replaces
	 * it with the provided rotation.
	 */
	bool setRotation(const Eigen::Quaternionf& rotation);
	bool setRotation(const Eigen::Vector3f& eulerAngles);
	bool setRotation(float eulerAngleX, float eulerAngleY, float eulerAngleZ);

	/* 
	 * Rotate this object using euler angles. These rotations rotate the object
	 * about its pivot point (in local coordinates).
	 */
	bool rotate(const Eigen::Vector3f& eulerAngles);
	bool rotate(float eulerAngleX, float eulerAngleY, float eulerAngleZ);
	
	float getPositionX() const;
	float getPositionY() const;
	float getPositionZ() const;
	Eigen::Vector3f getPosition() const;
	float getScaleX() const;
	float getScaleY() const;
	float getScaleZ() const;
	Eigen::Vector3f getScale() const;
	Eigen::Quaternionf getRotation() const;

	Color4f getColor() const;
	Color3f getColorSolid() const;

	float getPointSelectionDistance() const;
	float getLineSelectionDistance() const;
	bool isSelected() const;
	bool isVisible() const;
	String getName() const;

	/* 
	 * This function will use the internal transformation to transform a point
	 * in global coordinates to a point in this objects local coordinates.
	 */
	void transformPointToLocal(const Eigen::Vector3f& worldPoint, Eigen::Vector3f& localPoint) const;
	void transformPointToGlobal(const Eigen::Vector3f& localPoint, Eigen::Vector3f& globalPoint) const;
	void transformPointToGlobal(const Eigen::Vector3d& localPoint, Eigen::Vector3d& globalPoint) const;
	void transformLineToLocal(const Eigen::Vector3f& wPoint0, const Eigen::Vector3f& wPoint1, Eigen::Vector3f& lPoint0, Eigen::Vector3f& lPoint1) const;

	/* Direct transformation access */
	Math::Transformation getTransformation() const;
	Math::Transformation& transformation();

	/* Access to the Matrix(4x4) transformation. */
	const Eigen::Matrix4f& toMatrix4f();

protected:
	/* Simple name identifier */
	String name;

	/*
	 * State of visibility of this object. If visible = true then this object
	 * is visible and should be rendered; otherwise if visibile = false then
	 * this object is 'hidden' and should not be rendered (or rendered in a
	 * different way than visible objects).
	 */
	bool visible;

	/*
	 * Select state of this object. If selected = true then this object is
	 * considered selected or part of a special group of objects that have
	 * focus. If selected = false then the object is unselected.
	 */
	bool selected;
	float pointSelectionDistance, lineSelectionDistance;

	/* 
	 * The transform is maintained as a pointer to ensure that the graphics
	 * object hierarchy is independent from the mathematics library
	 * implementation details.
	 * 
	 * POINTER_OWNERSHIP: YES
	 */
	Math::Transformation transform;

	/*
	 * The color of a graphics object can be used to represent the entire
	 * object. This color spans the entire definition of the object, therefore
	 * it can be used by a renderer to distinguish it from other objects or
	 * background.
	 */
	Color4f color;

	// idk
	// Color Array?
	// Material - no
	// LineWidth, PointWidth - no
	// Smooth - no
};

}

}

#endif
